<script setup lang="ts">
/**
 * @prop {ITableColumn[]} column - 必填, 表头的列表, 数组每一项包含每一列的表头和这一列的配置
 *      @typedef {object} ITableColumn
 *          @property {string} label - 必填, 列标题
 *          @property {string} prop - 必填, 字段名称
 *          @property {boolean} hideColumu - 可选, 隐藏此列
 *          @property  element plus 官网在 el-table-colume 标签上的属性
 * @prop {ITableAction[]} action - 可选, 表格每一行末尾的操作按钮
 *      @typedef {object} ITableAction
 *          @property {string} label - 必填, 列标题
 *          @property {string} prop - 必填, 字段名称
 *          @property {string} type - 可选, 与 element plus 官网在 el-button 上的 type 属性一致
 *          @property {string} icon - 可选, 填写时显示图标, 目前仅适配icon-font, 填写其图标名
 *          @property {Partial<ButtonProps> & { customDisabled?: (row: any) => any }} option - 可选，支持 element plus 中按钮的所有属性和自定义禁用函数功能，函数入参为此行数据，自定义禁用优先级更高一些
 *          @property  element plus 官网在 el-button 标签上的属性
 * @prop {any[]} tableData - 表格显示的数据
 * 自定义配置
 * @prop {boolean} loading - 可选, 是否显示loading
 * @prop {number} fitHeight - 可选, 自动适配表格高度
 * @prop {boolean} showIndex - 可选, 是否显示序号列，默认为 false
 * @prop {boolean} showSelection - 可选, 是否显示选择框列，默认为 false
 * @prop {boolean} showFilter - 可选, 是否显示筛选排序按钮，默认为 false
 * 分页相关：
 * @prop {number} total - 可选, 分页组件总数
 * @prop {number} pageSize - 可选, 分页组件当前页大小
 * @prop {number} currentPage - 可选, 分页组件当前页
 * 原生配置相关：
 * @prop {Partial<TableProps<any>>} config - 可选, element plus 官网在 el-table 标签上的属性, table的配置项
 * @prop {Partial<TableColumnCtx<any>>} columuConfig - 可选, element plus 官网在 el-button 标签上的属性, 会设置到所有内容列
 *
 * @event actionClick - 用户点击表格每一行末尾按钮触发
 *      @property {object} payload - { method: string; data: any; }, method 为 action 中传入的 prop 字段, data 为此行数据
 * @event pageChange - 分页相关内容改变时触发
 *      @property {object} pageInfo - { pageSize: number; page: number; }
 * @event update:pageSize - 与父组件的当前页大小双向绑定
 * @event update:currentPage - 与父组件的当前页双向绑定
 * @event element-plus 支持 element plus 在 el-table 中所有事件，@fixme 但是使用时没有 ts 提示，目前不知道如何处理
 */
import {
  ElButton,
  ElIcon,
  ElTable,
  ElTableColumn,
  ElTooltip,
  PaginationProps,
  TableColumnCtx,
  TableInstance,
  TableProps,
  ElLoadingDirective as vLoading,
} from 'element-plus';
import { Ref, computed, ref, watchEffect } from 'vue';

import { IPageInfo, ITableAction, ITableColumn, ITableMenu } from '@/types';

import Icon from './Icon.vue';
import ListFilter from './ListFilter.vue';
import Pagination from './Pagination.vue';
import TableColumnChild from './TableColumnChild.vue';

const props = withDefaults(
  defineProps<{
    column: ITableColumn[];
    action?: ITableAction[];
    menu?: ITableMenu[];
    data: Record<string, any>[];
    loading?: boolean;
    fitHeight?: boolean;
    showIndex?: boolean;
    showSelection?: boolean;
    showFilter?: boolean;
    total?: number;
    pageSize?: number;
    currentPage?: number;
    pageConfig?: Partial<PaginationProps>;
    config?: Partial<TableProps<any>>;
    columuConfig?: Partial<TableColumnCtx<any>>;
    cellDefaultValue?: any;
  }>(),
  {
    action: () => [],
    menu: () => [],
    loading: false,
    fitHeight: false,
    showIndex: false,
    showSelection: false,
    showFilter: false,
  },
);

const emit = defineEmits<{
  (e: 'menuClick', method: string): void;
  (e: 'actionClick', method: string, data: any): void;
  (e: 'pageChange', pageInfo: IPageInfo): void;
  (e: 'update:pageSize', value: number): void;
  (e: 'update:currentPage', value: number): void;
}>();

const tableHeadCopy: Ref<ITableColumn[]> = ref([]);
watchEffect(() => {
  tableHeadCopy.value = [...props.column];
});

const pageSize = computed({
  get: () => {
    return props.pageSize || 10;
  },
  set: (val) => {
    emit('update:pageSize', val);
  },
});
const currentPage = computed({
  get: () => {
    return props.currentPage || 1;
  },
  set: (val) => {
    emit('update:currentPage', val);
  },
});

const actionClick = (method: string, data: any) => {
  emit('actionClick', method, data);
};
const menuClick = (method: string) => {
  emit('menuClick', method);
};

const actionLength = computed(() => {
  let length = 0;
  props.action.forEach((e) => {
    if (e.icon) {
      length += 26;
    } else {
      length += e.label.length * 12 + 24;
    }
  });
  length += props.action.length * 12 + 24;
  return length < 55 ? 55 : length;
});

const tableHeight = computed(() => {
  const showPage = props.pageSize ? 1 : 0;
  const showFilter = props.showFilter ? 1 : 0;
  return `height: calc(100% - ${showPage * 52}px - ${showFilter * 30}px)`;
});

const elTableRef = ref<TableInstance>();
const getTableInstance = () => {
  return elTableRef.value;
};
defineExpose({ getTableInstance });
</script>

<template>
  <div class="table-box" :class="fitHeight ? 'auto-height' : ''">
    <div v-if="props.showFilter || props.menu.length > 0" style="text-align: right">
      <el-tooltip v-for="item in menu" :key="item.prop" effect="dark" :content="item.label" placement="top">
        <el-icon @click="menuClick(item.prop)" :size="20" class="menu-icon">
          <Icon v-if="typeof item.icon === 'string'" :icon="item.icon"></Icon>
          <component v-else :is="item.icon"></component>
        </el-icon>
      </el-tooltip>
      <ListFilter v-if="props.showFilter" v-model:list="tableHeadCopy" />
    </div>
    <div :style="fitHeight ? tableHeight : ''">
      <el-table
        v-loading="props.loading"
        ref="elTableRef"
        :border="true"
        :height="fitHeight ? '100%' : undefined"
        :data="props.data"
        v-bind="{ ...props.config, ...$attrs }"
      >
        <!-- 多选列 -->
        <el-table-column v-if="props.showSelection" align="center" type="selection" width="55" />
        <!-- 序号列 -->
        <el-table-column v-if="props.showIndex" align="center" type="index" label="序号" width="55" />
        <!-- 表格内容 -->
        <template v-for="item in tableHeadCopy" :key="item.prop">
          <el-table-column
            v-if="!(item.hideColumu ?? false)"
            :show-overflow-tooltip="true"
            :formatter="(row, column, cellValue) => cellValue?.toString?.() || props.cellDefaultValue"
            v-bind="{ ...columuConfig, ...item }"
          >
            <template #header="scope" v-if="$slots[item.prop + 'Header']">
              <slot :name="item.prop + 'Header'" :data="scope.column">{{ scope.column.label }}</slot>
            </template>
            <template v-if="item.childHead" #default>
              <template v-for="e in item.childHead" :key="e.prop">
                <TableColumnChild :item="e" :columu-config="columuConfig" :cell-default-value="props.cellDefaultValue" />
              </template>
            </template>
            <template v-else-if="$slots[item.prop]" #default="scope">
              <slot :name="item.prop" :data="scope.row">{{ scope.row[item.prop] }}</slot>
            </template>
          </el-table-column>
        </template>
        <!-- 操作列 -->
        <el-table-column :fixed="'right'" v-if="action.length" label="操作" :width="actionLength" align="center">
          <template #header="scope" v-if="$slots['actionHeader']">
            <slot name="actionHeader" :data="scope.column">{{ scope.column.label }}</slot>
          </template>
          <template #default="scope">
            <template v-for="item in action" :key="item.prop">
              <el-tooltip :content="item.label" :disabled="!item.icon" effect="dark" placement="top">
                <el-button
                  size="small"
                  :class="item.icon ? 'icon-btn' : 'text-btn'"
                  :type="item.type"
                  @click="actionClick(item.prop, scope.row)"
                  v-bind="item.option"
                  :disabled="item.option?.customDisabled ? item.option.customDisabled(scope.row) : item.option?.disabled"
                >
                  <el-icon v-if="item.icon" :size="16">
                    <Icon v-if="typeof item.icon === 'string'" :icon="item.icon"></Icon>
                    <component v-else :is="item.icon"></component>
                  </el-icon>
                  <span v-else>{{ item.label }}</span>
                </el-button>
              </el-tooltip>
            </template>
          </template>
        </el-table-column>
        <template #empty v-if="$slots['empty']">
          <slot name="empty"></slot>
        </template>
      </el-table>
    </div>
    <div class="pagination" v-if="props.pageSize === undefined ? false : true">
      <Pagination
        :total="total || data.length"
        v-model:pageSize="pageSize"
        v-model:currentPage="currentPage"
        @pageChange="(e: IPageInfo) => emit('pageChange', e)"
        :config="pageConfig"
      />
    </div>
  </div>
</template>

<style lang="scss" scoped>
.auto-height {
  height: 100%;
  overflow: auto;
}
.table-box {
  .pagination {
    margin-top: 20px;
    display: flex;
    flex-direction: row-reverse;
  }
  .icon-btn {
    padding: 4px;
  }
  .menu-icon {
    margin: 0 10px 10px 0;
    cursor: pointer;
  }
}
</style>
